#include "NZipArchive.hpp"
#define MAXFNLENGTH (1024)
#define MAXBUFSIZE (4096)

NZipArchive::NZipArchive(void): _uf(NULL)
{
}

NZipArchive::NZipArchive( const char *filename ): _uf(NULL)
{
    open(filename);
}

NZipArchive::NZipArchive( const wchar_t *filename ): _uf(NULL)
{
    open(filename);
}

NZipArchive::~NZipArchive(void)
{
    close();
}

bool NZipArchive::open(const char *filename)
{
    close();
    _uf = unzOpen(filename, NULL);
    return _uf!=NULL;
}

bool NZipArchive::open(const wchar_t *filename)
{
    close();
    _uf = unzOpen(NULL, filename);
    return _uf!=NULL;
}

void NZipArchive::close()
{
    if (_uf)
    {
        unzClose(_uf);
        _uf = NULL;
    }
}

bool NZipArchive::isopen() const
{
    return _uf!=NULL;
}

size_t NZipArchive::filelist(std::vector<NArchiveFile> &flist)
{
    unz_global_info gi;
    if (unzGetGlobalInfo(_uf, &gi) == UNZ_OK)
    {
        flist.clear();
        CHAR fn[MAXFNLENGTH];
        unz_file_info fi;

        for (DWORD i=0;i<gi.number_entry;i++)
        {
            if (unzGetCurrentFileInfo(_uf, &fi, fn, MAXFNLENGTH, NULL, 0, NULL, 0) == UNZ_OK)
            {
                unz_file_pos	fpos;
                // if not folder
                if (fn[strlen(fn)-1]!='/')
                {
                    unzGetFilePos(_uf, &fpos);
                    NArchiveFile af(fn, NULL, fi.uncompressed_size, fpos.pos_in_zip_directory, fpos.num_of_file, NULL);
                    flist.push_back(af);
                }
            }
            unzGoToNextFile(_uf);
        }
    }
    return flist.size();
}

int NZipArchive::extract( NArchiveFile& naf )
{
    unz_file_pos    fpos;
    fpos.num_of_file = naf.Number;
    fpos.pos_in_zip_directory = naf.Offset;
    if (unzGoToFilePos(_uf, &fpos)==UNZ_OK)
    {
        DWORD   bread, pos = 0;
        CHAR    fn[MAXFNLENGTH];
        BYTE    tmp[MAXBUFSIZE];
        unz_file_info   fi;

        // what will happen if unzOpenCurrentFile removed?
        if ( unzGetCurrentFileInfo(_uf, &fi, fn, 0, NULL, 0, NULL, 0) == UNZ_OK && 
             fn[strlen(fn)-1]!='/' && 
             UNZ_OK == unzOpenCurrentFile(_uf))
        {
            // extract to buffer
            if (!naf.Alloc())
            {
                return NARC_ERROR;
            }
            do
            {
                bread = unzReadCurrentFile(_uf, tmp, MAXBUFSIZE);
                if (bread<0 || memcpy_s(naf.Data+pos, naf.Size-pos, tmp, bread)!=0)
                    return NARC_EXTRACTERR;
                pos += bread;
            }
            while (bread>0 && naf.Size-pos>=0);

            unzCloseCurrentFile(_uf);
            return NARC_OK;
        }
    }
    return NARC_ERROR;
}

bool NZipArchive::issupport( const char* filename ) const
{
    return _issupport(filename, NULL);
}

bool NZipArchive::issupport( const wchar_t* filename ) const
{
    return _issupport(NULL, filename);
}

bool NZipArchive::_issupport( const char* fn, const wchar_t* wfn ) const
{
    FILE* fin;
    if (wfn==NULL)
    {
        fopen_s(&fin, fn, "rb");
    } 
    else
    {
        _wfopen_s(&fin, wfn, L"rb");
    }

    if (fin!=NULL)
    {
        char buf[5] = {0};
        char head[] = "PK";
        fread_s(buf, sizeof(buf), sizeof(char), 2, fin);
        fclose(fin);
        return (strcmp(buf, head)==0);
    }
    return false;
}